/**
 *  Copyright 2010 Kyle O'Connor, Justin Arling, Mike Mercieca
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package edu.drexel.sd0910.ece01.aqmon.data.calc;

import java.io.IOException;
import java.util.Enumeration;

import org.apache.log4j.Logger;

import edu.drexel.sd0910.ece01.aqmon.data.DylosData;
import edu.drexel.sd0910.ece01.aqmon.data.WeatherData;
import edu.drexel.sd0910.ece01.aqmon.gui.MainModel;
import edu.drexel.sd0910.ece01.aqmon.manager.WeatherDataManager;
import edu.drexel.sd0910.ece01.aqmon.util.DataUtils;
import edu.drexel.sd0910.ece01.aqmon.util.OSUtils;

/**
 * Thread used to run calculations.
 * 
 * @author Kyle O'Connor
 *
 */
public class DataCalculatorThread implements Runnable {
	
	private final static Logger log = Logger.getLogger(DataCalculatorThread.class);
	
	private final static int PM25 = 0;
	private final static int PM10 = 1;
	
	private MainModel model;
	private DylosData monitorData;
	private WeatherData weatherData;

	/**
	 * Default constructor.
	 * 
	 * @param model
	 * @param monitorData
	 * @param weatherData
	 */
	public DataCalculatorThread(MainModel model, DylosData monitorData) {
		this.model = model;
		this.monitorData = monitorData;
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		log.debug("Data Calculation Thread started");
		
		try {
			// fetch latest weather data
			fetchWeatherData();
		} catch (Exception e1) {
			// an error occurred fetching weather data
			log.error("Error occurred fetching weather data" + OSUtils.getLineTerminator() + e1.toString());
			// exit data calculation thread
			return;
		}

		String nodeID = monitorData.getNodeID();
		String dateStr = monitorData.getDate().toString();
		int smallCount = monitorData.getParticleCounts()[PM25];
		int largeCount = monitorData.getParticleCounts()[PM10];
		double humidity = weatherData.getHumidity();
		double temp = weatherData.getTemperature();
		String condition = weatherData.getDescription();
		
		double PM25conc = DataUtils.convertPC25toMass(smallCount);
		double PM10conc = DataUtils.convertPC10toMass(largeCount);
		double humidityPercentage = humidity / 100;

		double PM25calc = PM25conc * humidityPercentage * getCorrectionFactor(humidity, condition);
		// no correction factor for PM10 measurements
		double PM10calc = PM25calc + PM10conc;

		try {
			model.getFileOutputManager().appendData(nodeID, dateStr, smallCount, largeCount, humidity, temp, condition, PM25calc, PM10calc);
		} catch (IOException e) {
			log.error(e.toString());
		}
		
		long tzOffset = model.getTimezoneOffset();
		long dateLong = monitorData.getDate().getTime() + tzOffset;
		
		CalculationResult result = new CalculationResult(dateLong, nodeID, PM25calc, PM10calc);
		model.getDataStore().storeData(result);
		
		log.debug("Data Calculation Thread finished");
	}
	
	private synchronized double getCorrectionFactor(double humidity, String conditions) {
		if ((conditions.equalsIgnoreCase(WeatherDataManager.RAIN_CONDITIONS))
				|| (conditions.equalsIgnoreCase(WeatherDataManager.HEAVY_RAIN_CONDITIONS))) {
			Enumeration<?> keys = model.getProperties().propertyNames();
			while (keys.hasMoreElements()) {
				String key = (String) keys.nextElement();
				if (key.startsWith("CF_WET_")) {
					String range = key.substring("CF_WET_".length());
					String[] ranges = range.split("-");
					int lower = Integer.parseInt(ranges[0]);
					int upper = Integer.parseInt(ranges[1]);

					if ((humidity >= lower) && (humidity <= upper)) {
						String factor = model.getProperties().getProperty(key);
						log.debug("Applying correction factor WET " + lower + "-" + upper + ": " + factor);
						return Double.parseDouble(factor);
					}
				}
			}
		} else {
			Enumeration<?> keys = model.getProperties().propertyNames();
			while (keys.hasMoreElements()) {
				String key = (String) keys.nextElement();
				if (key.startsWith("CF_DRY_")) {
					String range = key.substring("CF_DRY_".length());
					String[] ranges = range.split("-");
					int lower = Integer.parseInt(ranges[0]);
					int upper = Integer.parseInt(ranges[1]);

					if ((humidity >= lower) && (humidity <= upper)) {
						String factor = model.getProperties().getProperty(key);
						log.debug("Applying correction factor DRY " + lower + "-" + upper + ": " + factor);
						return Double.parseDouble(factor);
					}
				}
			}
		}
		return 1;
	}

	private void fetchWeatherData() throws Exception {
		double humidity = model.getWeatherDataManager().lookupCurrentHumidity();
		double temp = model.getWeatherDataManager().lookupCurrentTemperature();
		String cond = model.getWeatherDataManager().lookupCurrentConditionsDescription();
		weatherData = new WeatherData(humidity, temp, cond);
	}

}
